home *** CD-ROM | disk | FTP | other *** search
/ Mac Easy 2010 May / Mac Life Ubuntu.iso / casper / filesystem.squashfs / var / lib / python-support / python2.6 / glchess / chess / board.pyc (.txt) < prev    next >
Encoding:
Python Compiled Bytecode  |  2009-04-20  |  30.8 KB  |  1,142 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.6)
  3.  
  4. """Module implementing the chess rules.
  5.  
  6. To use create an instance of the chess board:
  7. >>> b = board.ChessBoard()
  8.  
  9. Board locations can be represented in two forms:
  10. o A 2-tuple containing the file and rank as integers (see below).
  11. o A string with the location in SAN format.
  12.  
  13. e.g. The black king is on the square (4,7) or 'e8'.
  14.  
  15. The chess board with rank and file numbers:
  16.  
  17.              Black Pieces
  18.  
  19.    +---+---+---+---+---+---+---+---+
  20. 7  |<R>|<N>|<B>|<Q>|<K>|<B>|<N>|<R>|
  21.    +---+---+---+---+---+---+---+---+
  22. 6  |<P>|<P>|<P>|<P>|<P>|<P>|<P>|<P>|
  23.    +---+---+---+---+---+---+---+---+
  24. 5  |   | . |   | . |   | . |   | . |
  25.    +---+---+---+---+---+---+---+---+
  26. 4  | . |   | . |   | . |   | . |   |
  27.    +---+---+---+---+---+---+---+---+
  28. 3  |   | . |   | . |   | . |   | . |
  29.    +---+---+---+---+---+---+---+---+
  30. 2  | . |   | . |   | . |   | . |   |
  31.    +---+---+---+---+---+---+---+---+
  32. 1  |-P-|-P-|-P-|-P-|-P-|-P-|-P-|-P-|
  33.    +---+---+---+---+---+---+---+---+
  34. 0  |-R-|-N-|-B-|-Q-|-K-|-B-|-N-|-R-|
  35.    +---+---+---+---+---+---+---+---+
  36.      0   1   2   3   4   5   6   7
  37.      
  38.              White Pieces
  39.              
  40. Pieces are moved by:
  41. >>> move = b.movePiece(board.WHITE, 'd1', 'd3')
  42. If the move is not None then the internal state is updated and the
  43. next player can move.
  44. If the result is None then the request is ignored.
  45.  
  46. A move can be checked if it is legal first by:
  47. >>> result = b.testMove(board.WHITE, 'd1', 'd3')
  48. The returns the same values as movePiece() except the internal state
  49. is never updated.
  50.  
  51. The location of pieces can be checked using:
  52. >>> piece = b.getPiece('e1')
  53. >>> pieces = b.getAlivePieces()
  54. >>> casualties = b.getDeadPieces()
  55. The locations are always in the 2-tuple format.
  56. These methods return references to the ChessPiece objects on the board.
  57.  
  58. The history of the game can be retrieved by passing a move number to
  59. the get*() methods. This number is the move count from the game start.
  60. It also supports negative indexing:
  61. 0  = board before game starts
  62. 1  = board after white's first move
  63. 2  = board after black's first move
  64. -1 = The last move
  65. e.g.
  66. To get the white pieces after whites second move.
  67. >>> pieces = b.getAlivePieces(3)
  68.  
  69. The ChessPiece objects are static per board. Thus references can be compared
  70. between move 0 and move N. Note promoted pieces are a new piece object.
  71.  
  72. When any piece is moved onPieceMoved() method is called. If the ChessBoard
  73. class is extended this signal can be picked up by the user. If movePiece()
  74. or testMove() is called while in this method an exception is raised.
  75. """
  76. __author__ = 'Robert Ancell <bob27@users.sourceforge.net>'
  77. __license__ = 'GNU General Public License Version 2'
  78. __copyright__ = 'Copyright 2005-2006  Robert Ancell'
  79. __all__ = [
  80.     'ChessPiece',
  81.     'ChessBoard',
  82.     'Move']
  83. import bitboard
  84. WHITE = 'White'
  85. BLACK = 'Black'
  86. PAWN = 'P'
  87. ROOK = 'R'
  88. KNIGHT = 'N'
  89. BISHOP = 'B'
  90. QUEEN = 'Q'
  91. KING = 'K'
  92.  
  93. class ChessPiece:
  94.     '''An object representing a chess piece'''
  95.     __colour = None
  96.     __type = None
  97.     
  98.     def __init__(self, colour, type):
  99.         """Constructor for a chess piece.
  100.         
  101.         'colour' is the piece colour (WHITE or BLACK).
  102.         'type' is the piece type (PAWN, ROOK, KNIGHT, BISHOP, QUEEN or KING).
  103.         """
  104.         self._ChessPiece__colour = colour
  105.         self._ChessPiece__type = type
  106.  
  107.     
  108.     def getColour(self):
  109.         '''Get the colour of this piece.
  110.         
  111.         Returns WHITE or BLACK.
  112.         '''
  113.         return self._ChessPiece__colour
  114.  
  115.     
  116.     def getType(self):
  117.         '''Get the type of this piece.
  118.         
  119.         Returns PAWN, ROOK, KNIGHT, BISHOP, QUEEN or KING.
  120.         '''
  121.         return self._ChessPiece__type
  122.  
  123.     
  124.     def __str__(self):
  125.         '''Returns a string representation of this piece'''
  126.         return self._ChessPiece__colour + ' ' + self._ChessPiece__type
  127.  
  128.     
  129.     def __repr__(self):
  130.         return '<%s>' % str(self)
  131.  
  132.  
  133.  
  134. class ChessPlayerState:
  135.     '''
  136.     '''
  137.     canShortCastle = True
  138.     canLongCastle = True
  139.     inCheck = False
  140.     
  141.     def __init__(self, state = None):
  142.         '''
  143.         '''
  144.         if state is None:
  145.             return None
  146.         self.canShortCastle = state.canShortCastle
  147.         self.canLongCastle = state.canLongCastle
  148.  
  149.     
  150.     def __eq__(self, state):
  151.         '''Compare two states are equal'''
  152.         if self.canShortCastle != state.canShortCastle:
  153.             return False
  154.         if self.canLongCastle != state.canLongCastle:
  155.             return False
  156.         return True
  157.  
  158.     
  159.     def __ne__(self, state):
  160.         return not (self == state)
  161.  
  162.  
  163.  
  164. class Move:
  165.     '''
  166.     '''
  167.     moves = []
  168.     victim = None
  169.     opponentInCheck = False
  170.     opponentCanMove = False
  171.     threeFoldRepetition = False
  172.     fiftyMoveRule = False
  173.  
  174.  
  175. class ChessBoardState:
  176.     '''
  177.     '''
  178.     moveNumber = 0
  179.     squares = None
  180.     casualties = None
  181.     lastMove = None
  182.     moves = None
  183.     enPassantSquare = None
  184.     whiteState = None
  185.     blackState = None
  186.     fiftyMoveCount = 0
  187.     threeFoldRepetition = False
  188.     whiteBitBoard = 0
  189.     blackBitBoard = 0
  190.     allBitBoard = 0
  191.     
  192.     def __init__(self, lastState = None):
  193.         """Constuctor for storing the state of a chess board.
  194.         
  195.         'lastState' is the previous board state
  196.                     or a dictionary containing the initial state of the board
  197.                     or None to start an empty board.
  198.  
  199.         Example:
  200.             
  201.         pawn = ChessPiece(WHITE, PAWN)
  202.         ChessBoardState({'a2': pawn, ...})
  203.         
  204.         Note if a dictionary is provided the casualties will only record the pieces
  205.         killed from this point onwards.
  206.         """
  207.         if lastState is None:
  208.             self.whiteBitBoard = 0
  209.             self.blackBitBoard = 0
  210.             self.allBitBoard = 0
  211.             self.moveNumber = 0
  212.             self.squares = { }
  213.             self.casualties = []
  214.             self.moves = []
  215.             self.whiteState = ChessPlayerState()
  216.             self.blackState = ChessPlayerState()
  217.         elif type(lastState) is dict:
  218.             self.moveNumber = 0
  219.             self.squares = { }
  220.             self.casualties = []
  221.             self.moves = []
  222.             self.whiteBitBoard = 0
  223.             self.blackBitBoard = 0
  224.             self.allBitBoard = 0
  225.             for coord, piece in lastState.iteritems():
  226.                 self.squares[coord] = piece
  227.                 field = bitboard.LOCATIONS[bitboard.getIndex(coord)]
  228.                 self.allBitBoard |= field
  229.             
  230.             self.whiteState = ChessPlayerState()
  231.             self.blackState = ChessPlayerState()
  232.         elif isinstance(lastState, ChessBoardState):
  233.             self.whiteBitBoard = lastState.whiteBitBoard
  234.             self.blackBitBoard = lastState.blackBitBoard
  235.             self.allBitBoard = lastState.allBitBoard
  236.             self.moveNumber = lastState.moveNumber + 1
  237.             self.squares = lastState.squares.copy()
  238.             self.casualties = lastState.casualties[:]
  239.             self.lastMove = lastState.lastMove
  240.             self.moves = lastState.moves[:]
  241.             self.enPassantSquare = lastState.enPassantSquare
  242.             self.whiteState = ChessPlayerState(lastState.whiteState)
  243.             self.blackState = ChessPlayerState(lastState.blackState)
  244.             self.fiftyMoveCount = lastState.fiftyMoveCount
  245.         else:
  246.             raise TypeError('ChessBoardState(oldState) or ChessBoardState({(0,0):pawn, ...})')
  247.         return None if piece.getColour() is WHITE else self
  248.  
  249.     
  250.     def addPiece(self, location, colour, pieceType):
  251.         piece = ChessPiece(colour, pieceType)
  252.         if not self.squares.has_key(location) is False:
  253.             raise AssertionError
  254.         if not type(location) == str:
  255.             raise AssertionError
  256.         self.squares[location] = piece
  257.         field = bitboard.LOCATIONS[bitboard.getIndex(location)]
  258.         self.allBitBoard |= field
  259.         return piece
  260.  
  261.     
  262.     def getPiece(self, location):
  263.         """Get the piece at a given location.
  264.         
  265.         'location' is the location in algebraic format (string).
  266.         
  267.         Return the piece at this location or None if there is no piece there.
  268.         """
  269.         if not type(location) is str or len(location) == 2:
  270.             raise AssertionError
  271.         
  272.         try:
  273.             return self.squares[location]
  274.         except KeyError:
  275.             len(location) == 2
  276.             len(location) == 2
  277.             return None
  278.  
  279.  
  280.     
  281.     def inCheck(self, colour):
  282.         """Test if the player with the given colour is in check.
  283.         
  284.         'colour' is the colour of the player to check.
  285.         
  286.         Return True if they are in check (or checkmate) or False otherwise.
  287.         """
  288.         for kingCoord, king in self.squares.iteritems():
  289.             if king.getType() != KING or king.getColour() != colour:
  290.                 continue
  291.             
  292.             if self.squareUnderAttack(colour, kingCoord):
  293.                 return True
  294.         
  295.         return False
  296.  
  297.     
  298.     def squareUnderAttack(self, colour, location, requirePiece = True):
  299.         """Check if a square is under attack according to FIDE chess rules (Article 3.1)
  300.         
  301.         'colour' is the colour considered to own this square.
  302.         'location' is the location to check.
  303.         'requirePiece' if True only considers this square under attack if there is a piece in it.
  304.         
  305.         Return True if there is an enemy piece that can attach this square.
  306.         """
  307.         if requirePiece and self.getPiece(location) is None:
  308.             return False
  309.         for enemyCoord, enemyPiece in self.squares.iteritems():
  310.             if enemyPiece.getColour() == colour:
  311.                 continue
  312.             
  313.             board = ChessBoardState(self)
  314.             if board.movePiece(enemyPiece.getColour(), enemyCoord, location, testCheck = False, applyMove = False):
  315.                 return True
  316.         
  317.         return False
  318.  
  319.     
  320.     def canMove(self, colour):
  321.         """Test if the player with the given colour is in checkmate.
  322.         
  323.         'colour' is the colour of the player to check.
  324.         
  325.         Return True if they are in checkmate or False otherwise.
  326.         """
  327.         for coord, piece in self.squares.iteritems():
  328.             if piece.getColour() != colour:
  329.                 continue
  330.             
  331.             for rank in 'abcdefgh':
  332.                 for file in '12345678':
  333.                     board = ChessBoardState(self)
  334.                     if board.movePiece(colour, coord, rank + file, applyMove = False):
  335.                         return True
  336.                 
  337.             
  338.         
  339.         return False
  340.  
  341.     
  342.     def _getSquareColour(self, coord):
  343.         return {
  344.             'a8': WHITE,
  345.             'b8': BLACK,
  346.             'c8': WHITE,
  347.             'd8': BLACK,
  348.             'e8': WHITE,
  349.             'f8': BLACK,
  350.             'g8': WHITE,
  351.             'h8': BLACK,
  352.             'a7': BLACK,
  353.             'b7': WHITE,
  354.             'c7': BLACK,
  355.             'd7': WHITE,
  356.             'e7': BLACK,
  357.             'f7': WHITE,
  358.             'g7': BLACK,
  359.             'h7': WHITE,
  360.             'a6': WHITE,
  361.             'b6': BLACK,
  362.             'c6': WHITE,
  363.             'd6': BLACK,
  364.             'e6': WHITE,
  365.             'f6': BLACK,
  366.             'g6': WHITE,
  367.             'h6': BLACK,
  368.             'a5': BLACK,
  369.             'b5': WHITE,
  370.             'c5': BLACK,
  371.             'd5': WHITE,
  372.             'e5': BLACK,
  373.             'f5': WHITE,
  374.             'g5': BLACK,
  375.             'h5': WHITE,
  376.             'a4': WHITE,
  377.             'b4': BLACK,
  378.             'c4': WHITE,
  379.             'd4': BLACK,
  380.             'e4': WHITE,
  381.             'f4': BLACK,
  382.             'g4': WHITE,
  383.             'h4': BLACK,
  384.             'a3': BLACK,
  385.             'b3': WHITE,
  386.             'c3': BLACK,
  387.             'd3': WHITE,
  388.             'e3': BLACK,
  389.             'f3': WHITE,
  390.             'g3': BLACK,
  391.             'h3': WHITE,
  392.             'a2': WHITE,
  393.             'b2': BLACK,
  394.             'c2': WHITE,
  395.             'd2': BLACK,
  396.             'e2': WHITE,
  397.             'f2': BLACK,
  398.             'g2': WHITE,
  399.             'h2': BLACK,
  400.             'a1': BLACK,
  401.             'b1': WHITE,
  402.             'c1': BLACK,
  403.             'd1': WHITE,
  404.             'e1': BLACK,
  405.             'f1': WHITE,
  406.             'g1': BLACK,
  407.             'h1': WHITE }[coord]
  408.  
  409.     
  410.     def sufficientMaterial(self):
  411.         '''Test if there are sufficient pieces to be able to perform checkmate.
  412.         
  413.         Return True if sufficient pieces to make checkmate or False otherwise.
  414.         '''
  415.         knightCount = 0
  416.         bishopCount = 0
  417.         for coord, piece in self.squares.iteritems():
  418.             pieceType = piece.getType()
  419.             if pieceType == PAWN and pieceType == ROOK or pieceType == QUEEN:
  420.                 return True
  421.             if pieceType == BISHOP:
  422.                 bishopCount += 1
  423.                 colour = self._getSquareColour(coord)
  424.                 bishopSquareColour = colour
  425.                 continue
  426.             None if bishopCount > 1 else None if pieceType == KNIGHT else pieceType == QUEEN
  427.         
  428.         return False
  429.  
  430.     allowedMoves = {
  431.         WHITE: {
  432.             PAWN: bitboard.WHITE_PAWN_MOVES,
  433.             ROOK: bitboard.ROOK_MOVES,
  434.             BISHOP: bitboard.BISHOP_MOVES,
  435.             KNIGHT: bitboard.KNIGHT_MOVES,
  436.             QUEEN: bitboard.QUEEN_MOVES,
  437.             KING: bitboard.WHITE_KING_MOVES },
  438.         BLACK: {
  439.             PAWN: bitboard.BLACK_PAWN_MOVES,
  440.             ROOK: bitboard.ROOK_MOVES,
  441.             BISHOP: bitboard.BISHOP_MOVES,
  442.             KNIGHT: bitboard.KNIGHT_MOVES,
  443.             QUEEN: bitboard.QUEEN_MOVES,
  444.             KING: bitboard.BLACK_KING_MOVES } }
  445.     
  446.     def movePiece(self, colour, start, end, promotionType = QUEEN, testCheck = True, allowSuicide = False, applyMove = True):
  447.         """Move a piece.
  448.         
  449.         'colour' is the colour of the player moving.
  450.         'start' is a the location to move from in algebraic format (string).
  451.         'end' is a the location to move to in algebraic format (string).
  452.         'promotionType' is the type of piece to promote to if required.
  453.         'testCheck' is a flag to control if the opponent will be in check after this move.
  454.         'allowSuicide' if True means a move is considered valid even
  455.                        if it would put the moving player in check.
  456.         'applyMove' is a flag to control if the move is applied to the board (True) or just tested (False).
  457.         
  458.         Returns the pieces moved in the form (result, moves).
  459.         The moves are a list containing tuples of the form (piece, start, end). If a piece was removed
  460.         'end' is None. If the result is successful the pieces on the board are modified.
  461.         If the move is illegal None is returned.
  462.         """
  463.         if not promotionType is not KING:
  464.             raise AssertionError
  465.         if not type(start) is str or len(start) == 2:
  466.             raise AssertionError
  467.         if not type(end) is str or len(end) == 2:
  468.             raise AssertionError
  469.         
  470.         try:
  471.             piece = self.squares[start]
  472.         except KeyError:
  473.             len(end) == 2
  474.             len(end) == 2
  475.             len(start) == 2
  476.             return None
  477.             promotionType is not KING
  478.  
  479.         if piece.getColour() is not colour:
  480.             return None
  481.         startIndex = bitboard.getIndex(start)
  482.         endIndex = bitboard.getIndex(end)
  483.         field = self.allowedMoves[colour][piece.getType()]
  484.         if field[startIndex] & bitboard.LOCATIONS[endIndex] == 0:
  485.             return None
  486.         if self.allBitBoard & bitboard.INBETWEEN_SQUARES[startIndex][endIndex]:
  487.             return None
  488.         if colour is WHITE:
  489.             enemyColour = BLACK
  490.             playerState = self.whiteState
  491.         elif colour is BLACK:
  492.             enemyColour = WHITE
  493.             playerState = self.blackState
  494.         elif not False:
  495.             raise AssertionError
  496.         piece.getColour() is not colour
  497.         originalPlayerState = ChessPlayerState(playerState)
  498.         whiteBitBoard = self.whiteBitBoard
  499.         blackBitBoard = self.blackBitBoard
  500.         allBitBoard = self.allBitBoard
  501.         
  502.         try:
  503.             target = self.squares[end]
  504.             if target.getColour() == colour:
  505.                 return None
  506.         except KeyError:
  507.             len(end) == 2
  508.             len(end) == 2
  509.             len(start) == 2
  510.             target = None
  511.         except:
  512.             promotionType is not KING
  513.  
  514.         victim = target
  515.         if colour == BLACK:
  516.             baseFile = '8'
  517.         else:
  518.             baseFile = '1'
  519.         enPassantSquare = None
  520.         moves = []
  521.         if piece.getType() is KING:
  522.             shortCastle = ('e' + baseFile, 'g' + baseFile)
  523.             longCastle = ('e' + baseFile, 'c' + baseFile)
  524.             playerState.canShortCastle = False
  525.             playerState.canLongCastle = False
  526.             moves.append((piece, start, end, False))
  527.         elif piece.getType() is ROOK:
  528.             if start == 'a' + baseFile:
  529.                 playerState.canLongCastle = False
  530.             elif start == 'h' + baseFile:
  531.                 playerState.canShortCastle = False
  532.             
  533.             moves.append((piece, start, end, False))
  534.         elif piece.getType() is PAWN:
  535.             if baseFile == '1':
  536.                 pawnFile = '2'
  537.                 marchFile = '3'
  538.                 farFile = '8'
  539.             else:
  540.                 pawnFile = '7'
  541.                 marchFile = '6'
  542.                 farFile = '1'
  543.             if (start[1] == '2' or end[1] == '4' or start[1] == '7') and end[1] == '5':
  544.                 enPassantSquare = start[0] + marchFile
  545.             
  546.             if start[0] != end[0]:
  547.                 if victim is None:
  548.                     if end != self.enPassantSquare:
  549.                         return None
  550.                     moves.append((self.lastMove[0], self.lastMove[2], self.lastMove[2], True))
  551.                 
  552.             elif victim is not None:
  553.                 return None
  554.             if end[1] == farFile:
  555.                 moves.append((piece, start, end, True))
  556.                 moves.append((ChessPiece(colour, promotionType), None, end, False))
  557.             else:
  558.                 moves.append((piece, start, end, False))
  559.         else:
  560.             moves.append((piece, start, end, False))
  561.         oldLastMove = self.lastMove
  562.         self.lastMove = (piece, start, end)
  563.         oldEnPassantSquare = self.enPassantSquare
  564.         self.enPassantSquare = enPassantSquare
  565.         if victim is not None:
  566.             moves.append((victim, end, end, True))
  567.         
  568.         for p, s, e, d in moves:
  569.             if s is None:
  570.                 continue
  571.             
  572.             self.squares.pop(s)
  573.             field = bitboard.LOCATIONS[bitboard.getIndex(s)]
  574.             self.whiteBitBoard &= ~field
  575.             self.blackBitBoard &= ~field
  576.             self.allBitBoard &= ~field
  577.         
  578.         for p, s, e, d in moves:
  579.             self.squares[e] = p
  580.             field = bitboard.LOCATIONS[bitboard.getIndex(e)]
  581.             self.allBitBoard |= field
  582.         
  583.         result = moves
  584.         return result
  585.  
  586.     
  587.     def __eq__(self, board):
  588.         '''Compare if two boards are the same'''
  589.         if len(self.squares) != len(board.squares):
  590.             return False
  591.         if self.enPassantSquare != board.enPassantSquare:
  592.             return False
  593.         if self.whiteState != board.whiteState or self.blackState != board.blackState:
  594.             return False
  595.         for coord, piece in self.squares.iteritems():
  596.             
  597.             try:
  598.                 p = board.squares[coord]
  599.             except KeyError:
  600.                 self.blackState != board.blackState
  601.                 self.blackState != board.blackState
  602.                 self.enPassantSquare != board.enPassantSquare
  603.                 return False
  604.                 len(self.squares) != len(board.squares)
  605.  
  606.             if piece.getType() is not p.getType() or piece.getColour() is not p.getColour():
  607.                 return False
  608.         
  609.         return True
  610.  
  611.     
  612.     def __ne__(self, board):
  613.         return not (self == board)
  614.  
  615.     
  616.     def __str__(self):
  617.         '''Covert the board state to a string'''
  618.         out = ''
  619.         blackSquare = False
  620.         for file in '87654321':
  621.             out += '       +---+---+---+---+---+---+---+---+\n'
  622.             out += '    ' + file + '  |'
  623.             blackSquare = not blackSquare
  624.             for rank in 'abcdefgh':
  625.                 blackSquare = not blackSquare
  626.                 
  627.                 try:
  628.                     piece = self.squares[rank + file]
  629.                 except:
  630.                     piece = None
  631.  
  632.                 if piece is None:
  633.                     if blackSquare:
  634.                         out += ' . '
  635.                     else:
  636.                         out += '   '
  637.                 else:
  638.                     s = piece.getType()
  639.                     if piece.getColour() is WHITE:
  640.                         s = '-' + s + '-'
  641.                     elif piece.getColour() is BLACK:
  642.                         s = '<' + s + '>'
  643.                     elif not False:
  644.                         raise AssertionError
  645.                     out += s
  646.                 out += '|'
  647.             
  648.             out += '\n'
  649.         
  650.         out += '       +---+---+---+---+---+---+---+---+\n'
  651.         out += '         a   b   c   d   e   f   g   h'
  652.         return out
  653.  
  654.  
  655.  
  656. class ChessBoard:
  657.     '''An object representing a chess board.
  658.     
  659.     This class contains a chess board and all its previous states.
  660.     '''
  661.     
  662.     def __init__(self, initialState = None):
  663.         '''Constructor for a chess board'''
  664.         self._ChessBoard__inCallback = False
  665.         if initialState is None:
  666.             self._ChessBoard__resetBoard()
  667.         else:
  668.             self._ChessBoard__boardStates = [
  669.                 initialState]
  670.  
  671.     
  672.     def onPieceMoved(self, piece, start, end, delete):
  673.         """Called when a piece is moved on the chess board.
  674.         
  675.         'piece' is the piece being moved.
  676.         'start' is the start location of the piece (tuple (file,rank) or None if the piece is being created.
  677.         'end' is the end location of the piece (tuple (file,rank))
  678.         'delete' is a flag to show if the piece should be deleted when it arrives there (boolean).
  679.         """
  680.         pass
  681.  
  682.     
  683.     def getPiece(self, location, moveNumber = -1):
  684.         """Get the piece at a given location.
  685.         
  686.         'location' is the board location to check in algebraic format (string).
  687.         'moveNumber' is the move to get the pieces from (integer).
  688.         
  689.         Return the piece (ChessPiece) at this location or None if there is no piece there.
  690.         Raises an IndexError exception if moveNumber is invalid.
  691.         """
  692.         return self._ChessBoard__boardStates[moveNumber].getPiece(location)
  693.  
  694.     
  695.     def getAlivePieces(self, moveNumber = -1):
  696.         """Get the alive pieces on the board.
  697.         
  698.         'moveNumber' is the move to get the pieces from (integer).
  699.         
  700.         Returns a dictionary of the alive pieces (ChessPiece) keyed by location.
  701.         Raises an IndexError exception if moveNumber is invalid.
  702.         """
  703.         state = self._ChessBoard__boardStates[moveNumber]
  704.         return state.squares.copy()
  705.  
  706.     
  707.     def getDeadPieces(self, moveNumber = -1):
  708.         """Get the dead pieces from the game.
  709.         
  710.         'moveNumber' is the move to get the pieces from (integer).
  711.         
  712.         Returns a list of the pieces (ChessPiece) in the order they were killed.
  713.         Raises an IndexError exception if moveNumber is invalid.
  714.         """
  715.         state = self._ChessBoard__boardStates[moveNumber]
  716.         return state.casualties[:]
  717.  
  718.     
  719.     def testMove(self, colour, start, end, promotionType = QUEEN, allowSuicide = False, moveNumber = -1):
  720.         """Test if a move is allowed.
  721.         
  722.         'colour' is the colour of the player moving.
  723.         'start' is a the location to move from in algebraic format (string).
  724.         'end' is a the location to move to in algebraic format (string).
  725.         'allowSuicide' if True means a move is considered valid even
  726.                        if it would put the moving player in check. This is
  727.                        provided for SAN move calculation.
  728.         
  729.         Returns the same as movePiece() except the move is not recorded.
  730.         """
  731.         return self.movePiece(colour, start, end, promotionType = promotionType, allowSuicide = allowSuicide, test = True, moveNumber = moveNumber)
  732.  
  733.     
  734.     def squareUnderAttack(self, colour, location, moveNumber = -1):
  735.         state = self._ChessBoard__boardStates[moveNumber]
  736.         return state.squareUnderAttack(colour, location)
  737.  
  738.     
  739.     def sufficientMaterial(self, moveNumber = -1):
  740.         '''Test if there are sufficient pieces to be able to perform checkmate.
  741.         
  742.         Return True if sufficient pieces to make checkmate or False otherwise.
  743.         '''
  744.         state = self._ChessBoard__boardStates[moveNumber]
  745.         return state.sufficientMaterial()
  746.  
  747.     
  748.     def movePiece(self, colour, start, end, promotionType = QUEEN, allowSuicide = False, test = False, moveNumber = -1):
  749.         """Move a piece.
  750.         
  751.         'colour' is the colour of the player moving.
  752.         'start' is a the location to move from in algebraic format (string).
  753.         'end' is a the location to move to in algebraic format (string).
  754.         'allowSuicide' if True means a move is considered valid even
  755.                        if it would put the moving player in check. This is
  756.                        provided for SAN move calculation.
  757.  
  758.         Return information about the move performed (Move) or None if the move is illegal.
  759.         """
  760.         if not self._ChessBoard__inCallback is False:
  761.             raise AssertionError
  762.         state = ChessBoardState(self._ChessBoard__boardStates[moveNumber])
  763.         if not state.movePiece(colour, start, end, promotionType = promotionType, allowSuicide = False):
  764.             return None
  765.         victim = None
  766.         for piece, start, end, delete in state.moves:
  767.             if not test:
  768.                 self._ChessBoard__onPieceMoved(piece, start, end, delete)
  769.                 continue
  770.             None if delete and piece.getColour() != colour else self._ChessBoard__inCallback is False
  771.         
  772.         sameCount = 0
  773.         for s in self._ChessBoard__boardStates:
  774.             if state == s:
  775.                 sameCount += 1
  776.                 if sameCount >= 2:
  777.                     state.threeFoldRepetition = True
  778.                     break
  779.                 
  780.             sameCount >= 2
  781.         
  782.         if colour is WHITE:
  783.             opponentColour = BLACK
  784.         else:
  785.             opponentColour = WHITE
  786.         if not test:
  787.             self._ChessBoard__boardStates.append(state)
  788.         
  789.         move = Move()
  790.         move.moves = state.moves
  791.         move.victim = victim
  792.         move.opponentInCheck = state.inCheck(opponentColour)
  793.         move.opponentCanMove = state.canMove(opponentColour)
  794.         move.threeFoldRepetition = state.threeFoldRepetition
  795.         move.fiftyMoveRule = state.fiftyMoveCount >= 50
  796.         return move
  797.  
  798.     
  799.     def undo(self):
  800.         '''Undo the last move'''
  801.         undoState = self._ChessBoard__boardStates[-1]
  802.         self._ChessBoard__boardStates = self._ChessBoard__boardStates[:-1]
  803.         for piece, start, end, delete in undoState.moves:
  804.             self._ChessBoard__onPieceMoved(piece, end, start, False)
  805.         
  806.  
  807.     
  808.     def __str__(self):
  809.         '''Returns a representation of the current board state'''
  810.         return str(self._ChessBoard__boardStates[-1])
  811.  
  812.     
  813.     def __onPieceMoved(self, piece, start, end, delete):
  814.         '''
  815.         '''
  816.         self._ChessBoard__inCallback = True
  817.         self.onPieceMoved(piece, start, end, delete)
  818.         self._ChessBoard__inCallback = False
  819.  
  820.     
  821.     def __addPiece(self, state, colour, pieceType, location):
  822.         """Add a piece into the board.
  823.         
  824.         'state' is the board state to add the piece into.
  825.         'colour' is the colour of the piece.
  826.         'pieceType' is the type of piece to add.
  827.         'location' is the start location of the piece in algebraic format (string).
  828.         """
  829.         piece = state.addPiece(location, colour, pieceType)
  830.         self._ChessBoard__onPieceMoved(piece, None, location, False)
  831.  
  832.     
  833.     def __resetBoard(self):
  834.         '''Set up the chess board.
  835.         
  836.         Any exisiting states are deleted.
  837.         The user will be notified of the piece deletions.
  838.         '''
  839.         initialState = ChessBoardState()
  840.         self._ChessBoard__boardStates = [
  841.             initialState]
  842.         secondRank = [
  843.             ('a', ROOK),
  844.             ('b', KNIGHT),
  845.             ('c', BISHOP),
  846.             ('d', QUEEN),
  847.             ('e', KING),
  848.             ('f', BISHOP),
  849.             ('g', KNIGHT),
  850.             ('h', ROOK)]
  851.         for rank, piece in secondRank:
  852.             self._ChessBoard__addPiece(initialState, WHITE, piece, rank + '1')
  853.             self._ChessBoard__addPiece(initialState, WHITE, PAWN, rank + '2')
  854.             self._ChessBoard__addPiece(initialState, BLACK, piece, rank + '8')
  855.             self._ChessBoard__addPiece(initialState, BLACK, PAWN, rank + '7')
  856.         
  857.  
  858.  
  859. if __name__ == '__main__':
  860.     p = ChessPiece(WHITE, QUEEN)
  861.     print p
  862.     print repr(p)
  863.     
  864.     def test_moves(name, colour, start, whitePieces, blackPieces, validResults):
  865.         print name + ':'
  866.         board = { }
  867.         for coord, piece in whitePieces.iteritems():
  868.             board[coord] = ChessPiece(WHITE, piece)
  869.         
  870.         for coord, piece in blackPieces.iteritems():
  871.             board[coord] = ChessPiece(BLACK, piece)
  872.         
  873.         s = ChessBoardState(board)
  874.         resultMatrix = { }
  875.         for rank in 'abcdefgh':
  876.             for file in '12345678':
  877.                 end = rank + file
  878.                 
  879.                 try:
  880.                     expected = validResults[end]
  881.                 except:
  882.                     expected = None
  883.  
  884.                 x = ChessBoardState(s)
  885.                 b = ChessBoard(x)
  886.                 move = b.movePiece(colour, start, end)
  887.                 resultMatrix[end] = move
  888.                 isAllowed = validResults.__contains__(end)
  889.                 if (move is None or isAllowed or move is not None) and not isAllowed:
  890.                     print 'Unexpected result: ' + str(start) + '-' + str(end)
  891.                     continue
  892.             
  893.         
  894.         out = ''
  895.         for file in '87654321':
  896.             out += '       +---+---+---+---+---+---+---+---+\n'
  897.             out += '    ' + file + '  |'
  898.             for rank in 'abcdefgh':
  899.                 coord = rank + file
  900.                 
  901.                 try:
  902.                     move = resultMatrix[coord]
  903.                 except:
  904.                     p = 'X'
  905.  
  906.                 if move is not None and move.opponentInCheck:
  907.                     if move.opponentCanMove:
  908.                         p = '+'
  909.                     else:
  910.                         p = '#'
  911.                 else:
  912.                     p = ' '
  913.                 piece = s.getPiece(rank + file)
  914.                 if piece is not None:
  915.                     p = piece.getType()
  916.                 
  917.                 piece = s.getPiece(rank + file)
  918.                 if piece is None:
  919.                     box = ' ' + p + ' '
  920.                 elif piece.getColour() is BLACK:
  921.                     box = '=' + p + '='
  922.                 elif piece.getColour() is WHITE:
  923.                     box = '-' + p + '-'
  924.                 
  925.                 out += box + '|'
  926.             
  927.             out += '\n'
  928.         
  929.         out += '       +---+---+---+---+---+---+---+---+\n'
  930.         out += '         a   b   c   d   e   f   g   h\n'
  931.         print out
  932.  
  933.     c = ChessBoard()
  934.     result = '       +---+---+---+---+---+---+---+---+\n    8  |<R>|<N>|<B>|<Q>|<K>|<B>|<N>|<R>|\n       +---+---+---+---+---+---+---+---+\n    7  |<P>|<P>|<P>|<P>|<P>|<P>|<P>|<P>|\n       +---+---+---+---+---+---+---+---+\n    6  |   | . |   | . |   | . |   | . |\n       +---+---+---+---+---+---+---+---+\n    5  | . |   | . |   | . |   | . |   |\n       +---+---+---+---+---+---+---+---+\n    4  |   | . |   | . |   | . |   | . |\n       +---+---+---+---+---+---+---+---+\n    3  | . |   | . |   | . |   | . |   |\n       +---+---+---+---+---+---+---+---+\n    2  |-P-|-P-|-P-|-P-|-P-|-P-|-P-|-P-|\n       +---+---+---+---+---+---+---+---+\n    1  |-R-|-N-|-B-|-Q-|-K-|-B-|-N-|-R-|\n       +---+---+---+---+---+---+---+---+\n         a   b   c   d   e   f   g   h'
  935.     if str(c) != result:
  936.         print 'Got:'
  937.         print str(c)
  938.         print 
  939.         print 'Expected:'
  940.         print result
  941.     
  942.     print str(c)
  943.     test_moves('Pawn', WHITE, 'e4', {
  944.         'e4': PAWN }, { }, [
  945.         'e5'])
  946.     test_moves('Pawn on base rank', WHITE, 'e2', {
  947.         'e2': PAWN }, { }, [
  948.         'e3',
  949.         'e4'])
  950.     test_moves('Rook', WHITE, 'e4', {
  951.         'e4': ROOK }, { }, [
  952.         'a4',
  953.         'b4',
  954.         'c4',
  955.         'd4',
  956.         'f4',
  957.         'g4',
  958.         'h4',
  959.         'e1',
  960.         'e2',
  961.         'e3',
  962.         'e5',
  963.         'e6',
  964.         'e7',
  965.         'e8'])
  966.     test_moves('Knight', WHITE, 'e4', {
  967.         'e4': KNIGHT }, { }, [
  968.         'd6',
  969.         'f6',
  970.         'g5',
  971.         'g3',
  972.         'f2',
  973.         'd2',
  974.         'c3',
  975.         'c5'])
  976.     test_moves('Bishop', WHITE, 'e4', {
  977.         'e4': BISHOP }, { }, [
  978.         'a8',
  979.         'b7',
  980.         'c6',
  981.         'd5',
  982.         'f3',
  983.         'g2',
  984.         'h1',
  985.         'b1',
  986.         'c2',
  987.         'd3',
  988.         'f5',
  989.         'g6',
  990.         'h7'])
  991.     test_moves('Queen', WHITE, 'e4', {
  992.         'e4': QUEEN }, { }, [
  993.         'a8',
  994.         'b7',
  995.         'c6',
  996.         'd5',
  997.         'f3',
  998.         'g2',
  999.         'h1',
  1000.         'b1',
  1001.         'c2',
  1002.         'd3',
  1003.         'f5',
  1004.         'g6',
  1005.         'h7',
  1006.         'a4',
  1007.         'b4',
  1008.         'c4',
  1009.         'd4',
  1010.         'f4',
  1011.         'g4',
  1012.         'h4',
  1013.         'e1',
  1014.         'e2',
  1015.         'e3',
  1016.         'e5',
  1017.         'e6',
  1018.         'e7',
  1019.         'e8'])
  1020.     test_moves('King', WHITE, 'e4', {
  1021.         'e4': KING }, { }, [
  1022.         'd5',
  1023.         'e5',
  1024.         'f5',
  1025.         'd4',
  1026.         'f4',
  1027.         'd3',
  1028.         'e3',
  1029.         'f3'])
  1030.     test_moves('Blocking', WHITE, 'd4', {
  1031.         'd4': QUEEN,
  1032.         'e4': PAWN,
  1033.         'd6': KNIGHT,
  1034.         'd2': ROOK,
  1035.         'f6': BISHOP,
  1036.         'e3': BISHOP,
  1037.         'b4': PAWN,
  1038.         'b2': PAWN,
  1039.         'a7': PAWN }, {
  1040.         'd8': KNIGHT,
  1041.         'c4': PAWN }, [
  1042.         'b6',
  1043.         'c5',
  1044.         'd5',
  1045.         'e5',
  1046.         'c4',
  1047.         'c3',
  1048.         'd3'])
  1049.     test_moves('Moving into check', WHITE, 'e4', {
  1050.         'e4': KING }, {
  1051.         'e6': ROOK }, [
  1052.         'd5',
  1053.         'f5',
  1054.         'd4',
  1055.         'f4',
  1056.         'd3',
  1057.         'f3'])
  1058.     test_moves('Held in check', WHITE, 'e4', {
  1059.         'e4': KING }, {
  1060.         'f6': ROOK }, [
  1061.         'd5',
  1062.         'e5',
  1063.         'd4',
  1064.         'd3',
  1065.         'e3'])
  1066.     test_moves('Putting opponent in check', WHITE, 'd3', {
  1067.         'd3': BISHOP }, {
  1068.         'd7': KING,
  1069.         'd6': ROOK }, [
  1070.         'a6',
  1071.         'b5',
  1072.         'c4',
  1073.         'e2',
  1074.         'f1',
  1075.         'b1',
  1076.         'c2',
  1077.         'e4',
  1078.         'f5',
  1079.         'g6',
  1080.         'h7'])
  1081.     test_moves('Putting opponent into checkmate', WHITE, 'c1', {
  1082.         'c1': BISHOP,
  1083.         'g1': ROOK,
  1084.         'a7': ROOK }, {
  1085.         'h8': KING }, [
  1086.         'b2',
  1087.         'a3',
  1088.         'd2',
  1089.         'e3',
  1090.         'f4',
  1091.         'g5',
  1092.         'h6'])
  1093.     test_moves('Cannot put opponent in check if we would go into check', WHITE, 'd3', {
  1094.         'd2': KING,
  1095.         'd3': BISHOP }, {
  1096.         'd7': KING,
  1097.         'd6': ROOK }, [])
  1098.     test_moves('Castle1', WHITE, 'e1', {
  1099.         'e1': KING,
  1100.         'a1': ROOK }, { }, [
  1101.         'd2',
  1102.         'e2',
  1103.         'f2',
  1104.         'd1',
  1105.         'f1',
  1106.         'c1'])
  1107.     test_moves('Castle2', BLACK, 'e8', { }, {
  1108.         'e8': KING,
  1109.         'h8': ROOK }, [
  1110.         'd7',
  1111.         'e7',
  1112.         'f7',
  1113.         'd8',
  1114.         'f8',
  1115.         'g8'])
  1116.     test_moves('Castle in check1', BLACK, 'e8', {
  1117.         'f1': ROOK }, {
  1118.         'e8': KING,
  1119.         'h8': ROOK }, [
  1120.         'd7',
  1121.         'e7',
  1122.         'd8'])
  1123.     test_moves('Castle in check2', BLACK, 'e8', {
  1124.         'e1': ROOK }, {
  1125.         'e8': KING,
  1126.         'h8': ROOK }, [
  1127.         'd7',
  1128.         'd8',
  1129.         'f7',
  1130.         'f8'])
  1131.     test_moves('Castle in check3', BLACK, 'e8', {
  1132.         'h1': ROOK }, {
  1133.         'e8': KING,
  1134.         'h8': ROOK }, [
  1135.         'd7',
  1136.         'e7',
  1137.         'f7',
  1138.         'd8',
  1139.         'f8',
  1140.         'g8'])
  1141.  
  1142.